/*
 * 代号：凤凰
 * http://www.jphenix.org
 * 2014-06-10
 * V4.0
 */
/*
 * Javassist, a Java-bytecode translator toolkit.
 * Copyright (C) 2004 Bill Burke. All Rights Reserved.
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License.  Alternatively, the contents of this file may be used under
 * the terms of the GNU Lesser General Public License Version 2.1 or later,
 * or the Apache License Version 2.0.
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 */
package com.jphenix.clazz.annotation;

import com.jphenix.clazz.ConstPool;
import com.jphenix.clazz.Descriptor;
import com.jphenix.standard.docs.ClassInfo;

import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Set;

/**
 * The <code>annotation</code> structure.
 *
 * <p>An instance of this class is returned by
 * <code>getAnnotations()</code> in <code>AnnotationsAttribute</code>
 * or in <code>ParameterAnnotationsAttribute</code>.
 *
 * @see //javassist.bytecode.AnnotationsAttribute#getAnnotations()
 * @see //javassist.bytecode.ParameterAnnotationsAttribute#getAnnotations()
 * @see MemberValue
 * @see MemberValueVisitor
 * @see //AnnotationsWriter
 *
 * @author <a href="mailto:bill@jboss.org">Bill Burke</a>
 * @author Shigeru Chiba
 * @author <a href="mailto:adrian@jboss.org">Adrian Brock</a>
 */
@ClassInfo({"2017-02-04 22:20","注释对象"})
public class Annotation {
    static class Pair {
        int name;
        MemberValue value;
    }

    ConstPool pool;
    int typeIndex;
    @SuppressWarnings("rawtypes")
    LinkedHashMap members;    // this sould be LinkedHashMap
                        // but it is not supported by JDK 1.3.

    /**
     * Constructs an annotation including no members.  A member can be
     * later added to the created annotation by <code>addMemberValue()</code>. 
     *
     * @param type  the index into the constant pool table.
     *              the entry at that index must be the
     *              <code>CONSTANT_Utf8_Info</code> structure
     *              repreenting the name of the annotation interface type.
     * @param cp    the constant pool table.
     *
     * @see #addMemberValue(String, MemberValue)
     */
    public Annotation(int type, ConstPool cp) {
        pool = cp;
        typeIndex = type;
        members = null;
    }

    /**
     * Constructs an annotation including no members.  A member can be
     * later added to the created annotation by <code>addMemberValue()</code>. 
     *
     * @param typeName  the name of the annotation interface type.
     * @param cp        the constant pool table.
     *
     * @see #addMemberValue(String, MemberValue)
     */
    public Annotation(String typeName, ConstPool cp) {
        this(cp.addUtf8Info(Descriptor.of(typeName)), cp);
    }


    /**
     * Adds a new member.
     *
     * @param nameIndex     the index into the constant pool table.
     *                      The entry at that index must be
     *                      a <code>CONSTANT_Utf8_info</code> structure.
     *                      structure representing the member name.
     * @param value         the member value.
     */
    public void addMemberValue(int nameIndex, MemberValue value) {
        Pair p = new Pair();
        p.name = nameIndex;
        p.value = value;
        addMemberValue(p);
    }

    /**
     * Adds a new member.
     *
     * @param name      the member name.
     * @param value     the member value.
     */
    @SuppressWarnings({ "rawtypes", "unchecked" })
	public void addMemberValue(String name, MemberValue value) {
        Pair p = new Pair();
        p.name = pool.addUtf8Info(name);
        p.value = value;
        if (members == null) {
            members = new LinkedHashMap();
        }

        members.put(name, p);
    }

    @SuppressWarnings({ "unchecked", "rawtypes" })
	private void addMemberValue(Pair pair) {
        String name = pool.getUtf8Info(pair.name);
        if (members == null) {
            members = new LinkedHashMap();
        }

        members.put(name, pair);
    }

    /**
     * Returns a string representation of the annotation.
     */
    @Override
    @SuppressWarnings({ "rawtypes" })
    public String toString() {
        StringBuffer buf = new StringBuffer("@");
        buf.append(getTypeName());
        if (members != null) {
            buf.append("(");
            Iterator mit = members.keySet().iterator();
            while (mit.hasNext()) {
                String name = (String)mit.next();
                buf.append(name).append("=").append(getMemberValue(name));
                if (mit.hasNext()) {
                    buf.append(", ");
                }
            }
            buf.append(")");
        }

        return buf.toString();
    }

    /**
     * Obtains the name of the annotation type.
     * 
     * @return the type name
     */
    public String getTypeName() {
        return Descriptor.toClassName(pool.getUtf8Info(typeIndex));
    }

    /**
     * Obtains all the member names.
     *
     * @return null if no members are defined.
     */
    @SuppressWarnings({ "rawtypes" })
    public Set getMemberNames() {
        if (members == null) {
            return null;
        } else {
            return members.keySet();
        }
    }

    /**
     * Obtains the member value with the given name.
     *
     * <p>If this annotation does not have a value for the
     * specified member,
     * this method returns null.  It does not return a
     * <code>MemberValue</code> with the default value.
     * The default value can be obtained from the annotation type.
     *
     * @param name the member name
     * @return null if the member cannot be found or if the value is
     * the default value.
     *
     * @see //javassist.bytecode.AnnotationDefaultAttribute
     */
    public MemberValue getMemberValue(String name) {
        if (members == null) {
            return null;
        } else {
            Pair p = (Pair)members.get(name);
            if (p == null) {
                return null;
            } else {
                return p.value;
            }
        }
    }

    /**
     * Constructs an annotation-type object representing this annotation.
     * For example, if this annotation represents <code>@Author</code>,
     * this method returns an <code>Author</code> object.
     * 
     * @return the annotation
     * @throws ClassNotFoundException   if the class cannot found.
     * @throws Exception         if the class linkage fails.
     */
    public Object toAnnotationType() throws Exception {
    	ClassLoader cl = this.getClass().getClassLoader();
        return AnnotationImpl.make(cl,
                        MemberValue.loadClass(cl, getTypeName()),this);
    }

    /**
     * Returns true if the given object represents the same annotation
     * as this object.  The equality test checks the member values.
     */
    @Override
    @SuppressWarnings({ "rawtypes" })
    public boolean equals(Object obj) {
        if (obj == this) {
            return true;
        }
        if (obj == null || obj instanceof Annotation == false) {
            return false;
        }
        
        Annotation other = (Annotation) obj;

        if (getTypeName().equals(other.getTypeName()) == false) {
            return false;
        }

        LinkedHashMap otherMembers = other.members;
        if (members == otherMembers) {
            return true;
        } else if (members == null) {
            return otherMembers == null;
        } else
            if (otherMembers == null) {
                return false;
            } else {
                return members.equals(otherMembers);
            }
    }
}
